home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 May: Tool Chest / Developer CD Series Tool Chest (Apple Computer)(May 1999).iso / What's New? / Technical Documentaion / Macintosh Technotes and Q&As / technotes / tn / fdp_1085.hqx / FinderDragPro / FinderDragPro.c < prev    next >
Encoding:
Text File  |  1997-02-26  |  21.1 KB  |  918 lines

  1.     //
  2.     //    "FinderDragPro.c"
  3.     //
  4.  
  5. #define OLDROUTINELOCATIONS        0
  6. #define OLDROUTINENAMES            0
  7. #define SystemSevenOrLater        1
  8.  
  9. #include "DragManagerAdditions.h"
  10. #include "Technote.h"
  11. #include "FinderStuff.h"
  12. #include "FileCopy.h"
  13. #include "DirectoryCopy.h"
  14.  
  15. #ifndef __LOWMEM__
  16. #    include <LowMem.h>
  17. #endif
  18.  
  19. #ifndef __FONTS__
  20. #    include <Fonts.h>
  21. #endif
  22.  
  23. #ifndef __DIALOGS__
  24. #    include <Dialogs.h>
  25. #endif
  26.  
  27. #ifndef __DRAG__
  28. #    include <Drag.h>
  29. #endif
  30.  
  31. #ifndef __ICONS__
  32. #    include <Icons.h>
  33. #endif
  34.  
  35. #ifndef __PLSTRINGFUNCS__
  36. #    include <PLStringFuncs.h>
  37. #endif
  38.  
  39. #ifndef __TEXTUTILS__
  40. #    include <TextUtils.h>
  41. #endif
  42.  
  43. #ifndef __GESTALT__
  44. #    include <Gestalt.h>
  45. #endif
  46.  
  47. #ifndef __QDOFFSCREEN__
  48. #    include <QDOffscreen.h>
  49. #endif
  50.  
  51. static Boolean            gQuitting;                // set when it's time to quit
  52. static FontInfo            gFontInfo;                // cache for Geneva 9 info
  53. static Rect                gIconLimitRect;            // window portRect without text area
  54. static Boolean            gHaveTranslucence;        // can we call SetDragImage?
  55. static FSSpec            gDropLocation;            // cache where user dropped
  56. static HFSFlavor        gHFSFlavor;                // data dragged in, re-used when dragging out
  57. static DragReference    gApprovedDragRef;        // tracking handler approves, receive handler confirms
  58. static ItemReference    gItemRef;                // valid when gApprovedDragRef is being received
  59. static Handle            gIconSuite;                // cached from flavorTypeHFS dragged in
  60. static FSSpec            gDropLocFSS;
  61. static FSSpec            gCopyTarget;
  62.  
  63. static pascal OSErr InitMac (void)
  64. {
  65.     //
  66.     //    Initialize the toolbox and find out
  67.     //    whether we can call SetDragImage.
  68.     //
  69.  
  70.     OSErr err = noErr;
  71.  
  72.     MaxApplZone ( );
  73.     if (!(err = MemError ( )))
  74.     {
  75.         long gestaltResponse;
  76.  
  77.         InitGraf (&(qd.thePort));
  78.         InitFonts ( );
  79.         InitWindows ( );
  80.         InitMenus ( );
  81.         TEInit ( );
  82.         InitDialogs (nil);
  83.  
  84.         if (!(err = Gestalt (gestaltDragMgrAttr,&gestaltResponse)))
  85.         {
  86.             gHaveTranslucence = !!(gestaltResponse & (1 << gestaltDragMgrHasImageSupport));
  87.         }
  88.     }
  89.  
  90.     return err;
  91. }
  92.  
  93. static pascal void MakeIconRect (Rect *iconRect)
  94. {
  95.     //
  96.     //    Calculate a rectangle sized as a 32x32 icon in the center
  97.     //    of the cached icon limit rect (see gIconLimitRect above).
  98.     //
  99.  
  100.     short    halfLimitHeight        = (gIconLimitRect.bottom - gIconLimitRect.top) / 2,
  101.             halfLimitWidth        = (gIconLimitRect.right - gIconLimitRect.left) / 2;
  102.  
  103.     SetRect (iconRect,0,0,32,32);
  104.     OffsetRect (iconRect, halfLimitWidth - 16, halfLimitHeight - 16);
  105. }
  106.  
  107. static pascal OSErr MakeDragTranslucent
  108.     (DragReference dragRef, const Rect *iconRect, GWorldPtr *imageGWorld, RgnHandle *maskRgn)
  109. {
  110.     //
  111.     //    Add a translucent image of the Finder icon to the DragReference.
  112.     //    Produces a GWorld and a Rgn to be disposed by caller after TrackDrag.
  113.     //
  114.  
  115.     OSErr err = noErr;
  116.  
  117.     *imageGWorld    = nil;
  118.     *maskRgn        = nil;
  119.  
  120.     if (gHaveTranslucence)
  121.     {
  122.         const    short        pixelDepth    = 8;
  123.                 Rect        imageRect    = *iconRect;
  124.                 Point        offsetPt;
  125.  
  126.         SetPt (&offsetPt, imageRect.left, imageRect.top);
  127.         OffsetRect (&imageRect, -(imageRect.left), -(imageRect.top));
  128.         LocalToGlobal (&offsetPt);
  129.  
  130.         err = NewGWorld (imageGWorld, pixelDepth, &imageRect, nil, nil, 0);
  131.         if (err == memFullErr)
  132.             err = NewGWorld (imageGWorld, pixelDepth, &imageRect, nil, nil, useTempMem);
  133.         if (!err)
  134.         {
  135.             *maskRgn = NewRgn ( );
  136.             if (!(err = MemError ( )))
  137.             {
  138.                 if (!(err = IconSuiteToRgn (*maskRgn,&imageRect,kAlignNone,gIconSuite)))
  139.                 {
  140.                     GDHandle        saveDevice;
  141.                     CGrafPtr        savePort;
  142.                     PixMapHandle    imagePixMap        = GetGWorldPixMap (*imageGWorld);
  143.  
  144.                     GetGWorld (&savePort, &saveDevice);
  145.                     (void) LockPixels (imagePixMap);
  146.                     SetGWorld (*imageGWorld, nil);
  147.                     EraseRect (&(qd.thePort->portRect));
  148.                     err = PlotIconSuite (&imageRect,kAlignNone,kTransformNone,gIconSuite);
  149.                     UnlockPixels (imagePixMap);
  150.                     SetGWorld (savePort, saveDevice);
  151.  
  152.                     if (!err)
  153.                         err = SetDragImage (dragRef, imagePixMap, *maskRgn, offsetPt, dragStandardImage);
  154.                 }
  155.             }
  156.         }
  157.  
  158.         if (err)
  159.         {
  160.             if (*imageGWorld)
  161.             {
  162.                 DisposeGWorld (*imageGWorld);
  163.                 *imageGWorld = nil;
  164.             }
  165.  
  166.             if (*maskRgn)
  167.             {
  168.                 DisposeRgn (*maskRgn);
  169.                 *maskRgn = nil;
  170.             }
  171.         }
  172.     }
  173.  
  174.     return err;
  175. }
  176.  
  177. static pascal OSErr RedundantMakeHFSFlavor (HFSFlavor *hfsFlavorP)
  178. {
  179.     //
  180.     //    We could assume that any HFSFlavor we're handed thru
  181.     //    our drag receive handler is valid. However:
  182.     //
  183.     //        [1] It might have become stale since we received it.
  184.     //
  185.     //        [2] We need an excuse to call MakeHFSFlavor so we
  186.     //            can test it before pasting it into the Technote.
  187.     //
  188.     //    Thus, this function confirms/refreshes the HFSFlavor data;
  189.     //    generally we're passed a pointer to the global HFSFlavor cache,
  190.     //    but this code doesn't know it.
  191.     //
  192.  
  193.     OSErr err = noErr;
  194.  
  195.     //
  196.     //    Clear some fields so we can see that MakeHFSFlavor
  197.     //    did its job.
  198.     //
  199.  
  200.     hfsFlavorP->fileType        =
  201.     hfsFlavorP->fileCreator        =
  202.     hfsFlavorP->fdFlags            = 0;
  203.  
  204.     err = MakeHFSFlavor (    hfsFlavorP->fileSpec.vRefNum,
  205.                             hfsFlavorP->fileSpec.parID,
  206.                             hfsFlavorP->fileSpec.name,
  207.                             hfsFlavorP                        );
  208.  
  209.     return err;
  210. }
  211.  
  212. static pascal OSErr WakeUpCurrentProcess (void)
  213. {
  214.     ProcessSerialNumber curPSN = { 0, kCurrentProcess };
  215.     return WakeUpProcess (&curPSN); // "post" a null event
  216. }
  217.  
  218. static pascal OSErr CopyDroppedFileOrFolder
  219.     (DragReference dragRef, ItemReference itemRef, const PromiseHFSFlavor *phfs)
  220. {
  221.     //
  222.     //    Figure out where the user dropped the file or folder and create a dummy.
  223.     //    Then prepare to send an AppleEvent to Finder to overwrite the dummy.
  224.     //    Don't send the AppleEvent yet because we need to delete the dummy file
  225.     //    after TrackDrag completes so that Drag Manager doesn't display rejection
  226.     //    feedback to the user. Look at the code surrounding
  227.     //    AddTranslucentIconAndDrag's call to TrackDrag for more info.
  228.     //
  229.  
  230.     OSErr err = noErr;
  231.  
  232.     if (!(err = GetDropDirectory (dragRef,&gDropLocFSS)))
  233.     {
  234.         long dropDirID;
  235.  
  236.         if (!(err = GetDirID (&gDropLocFSS,&dropDirID)))
  237.             if (!(err = FSMakeFSSpec (gDropLocFSS.vRefNum, dropDirID, gHFSFlavor.fileSpec.name, &gCopyTarget)))
  238.                 err = dupFNErr;
  239.             else if (err == fnfErr)
  240.                 if (!(err = CreatePromisedFileOrFolder (phfs,&gCopyTarget,smSystemScript)))
  241.                     err = SetPromisedHFSFlavorData (dragRef,itemRef,phfs,&gCopyTarget);
  242.  
  243.         if (err)
  244.         {
  245.             gDropLocFSS.vRefNum        = 0;
  246.             gCopyTarget.vRefNum        = 0;
  247.         }
  248.     }
  249.  
  250.     return err;
  251. }
  252.  
  253. static pascal OSErr MyDragSendDataProc
  254.     (FlavorType flavorType, void *, ItemReference itemRef, DragReference dragRef)
  255. {
  256.     //
  257.     //    This function fulfills the promise of the promisedFlavor data.
  258.     //    See comments in CopyDroppedFileOrFolder.
  259.     //
  260.  
  261.     OSErr err = noErr;
  262.  
  263.     PromiseHFSFlavor  phfs;
  264.     Size              size = sizeof (phfs);
  265.  
  266.     if (!(err = GetFlavorData (dragRef,itemRef,flavorTypePromiseHFS,&phfs,&size,0)))
  267.     {
  268.         if (size != sizeof (phfs))
  269.             err = cantGetFlavorErr;
  270.         else if (flavorType == phfs.promisedFlavor)
  271.         {
  272.             Boolean shouldCopy;
  273.  
  274.             if (!(err = ShouldCopyToDropLoc (dragRef,flavorType,&shouldCopy)))
  275.             {
  276.                 if (shouldCopy)
  277.                 {
  278.                     SetCursor (*GetCursor (watchCursor));
  279.                     err = CopyDroppedFileOrFolder (dragRef,itemRef,&phfs);
  280.                 }
  281.                 else
  282.                 {
  283.                     err = SetPromisedHFSFlavorData
  284.                         (dragRef,itemRef,&phfs,&(gHFSFlavor.fileSpec));
  285.                 }
  286.             }
  287.         }
  288.     }
  289.  
  290.     return err;
  291. }
  292.  
  293. static pascal OSErr AttachSendDataProc (DragReference dragRef)
  294. {
  295.     //
  296.     //    Cretaes a UPP for the SendDataProc if necessary
  297.     //    and attaches it to the given DragReference.
  298.     //
  299.  
  300.     OSErr err = noErr;
  301.  
  302.     static DragSendDataProc dragSendDataProc;
  303.  
  304.     if (!dragSendDataProc)
  305.         dragSendDataProc = NewDragSendDataProc (MyDragSendDataProc);
  306.     if (!dragSendDataProc)
  307.         err = nilHandleErr;
  308.     else
  309.         err = SetDragSendProc (dragRef,dragSendDataProc,nil);
  310.  
  311.     return err;
  312. }
  313.  
  314. static pascal OSErr AddTranslucentIconAndDrag
  315.     (DragReference dragRef, const EventRecord *event, const Rect *iconRect)
  316. {
  317.     //
  318.     //    Add an appropriate icon to the DragReference. Drag.
  319.     //    Copy any necessary files as result from the drag.
  320.     //
  321.  
  322.     OSErr err = noErr;
  323.  
  324.     RgnHandle dragRgn = NewRgn ( );
  325.     if (!(err = MemError ( )))
  326.     {
  327.         if (!(err = IconSuiteToRgn (dragRgn,iconRect,kAlignNone,gIconSuite)))
  328.         {
  329.             RgnHandle insetRgn = NewRgn ( );
  330.             if (!(err = MemError ( )))
  331.             {
  332.                 Point        globalOrigin;
  333.                 GWorldPtr    imageGWorld;
  334.                 RgnHandle    maskRgn;
  335.  
  336.                 CopyRgn (dragRgn,insetRgn);
  337.                 InsetRgn (insetRgn,1,1);
  338.                 DiffRgn (dragRgn,insetRgn,dragRgn);
  339.                 SetPt (&globalOrigin,qd.thePort->portRect.left,qd.thePort->portRect.top);
  340.                 LocalToGlobal (&globalOrigin);
  341.                 OffsetRgn (dragRgn,globalOrigin.h,globalOrigin.v);
  342.  
  343.                 (void) MakeDragTranslucent (dragRef,iconRect,&imageGWorld,&maskRgn);
  344.                 // ignore errors; we don't care very much; dispose whatever we get after
  345.  
  346.                 err = TrackDrag (dragRef,event,dragRgn);
  347.  
  348.                 DisposeRgn (insetRgn);
  349.                 if (!err) err = MemError ( );
  350.  
  351.                 if (imageGWorld)
  352.                     DisposeGWorld (imageGWorld);
  353.                 if (maskRgn)
  354.                     DisposeRgn (maskRgn);
  355.             }
  356.         }
  357.         DisposeRgn (dragRgn);
  358.         if (!err) err = MemError ( );
  359.     }
  360.  
  361.     return err;
  362. }
  363.  
  364. static pascal OSErr DragOut (const EventRecord *event, const Rect *iconRect)
  365. {
  366.     //
  367.     //    Administrates the process of dragging the icon out of the window.
  368.     //    If the option key is being held down, use flavorTypePromiseHFS
  369.     //    so drag receivers can ask for a copy. Make the drag translucent.
  370.     //    Copy the file via MoreFiles if appropriate.
  371.     //
  372.  
  373.     OSErr err = noErr;
  374.  
  375.     if (WaitMouseMoved (event->where))
  376.     {
  377.         DragReference dragRef;
  378.  
  379.         if (!(err = NewDrag (&dragRef)))
  380.         {
  381.             OSErr err2 = noErr;
  382.  
  383.             if (!(event->modifiers & optionKey))
  384.             {
  385.                 if (!(err = RedundantMakeHFSFlavor (&gHFSFlavor)))
  386.                     err = AddDragItemFlavor
  387.                         (dragRef, 0, flavorTypeHFS, &gHFSFlavor, sizeof (gHFSFlavor), 0);
  388.             }
  389.             else if (!(err = AttachSendDataProc (dragRef)))
  390.             {
  391.                 FlavorType promisedFlavor =
  392.                     (event->modifiers & cmdKey) ? kPromisedFlavorFindFile : kPromisedFlavor;
  393.                 err = AddDragItemFlavorTypePromiseHFS
  394.                     (dragRef,0,gHFSFlavor.fileType,gHFSFlavor.fileCreator,
  395.                         gHFSFlavor.fdFlags,promisedFlavor);
  396.             }
  397.  
  398.             if (!err)
  399.             {
  400.                 gCopyTarget.vRefNum        = 0;
  401.                 gDropLocFSS.vRefNum        = 0;
  402.  
  403.                 if (!(err = AddTranslucentIconAndDrag (dragRef,event,iconRect)))
  404.                 {
  405.                     if (!err && gDropLocFSS.vRefNum && gCopyTarget.vRefNum)
  406.                     {
  407.                         if (!(err = FSpDelete (&gCopyTarget)))
  408.                         {
  409.                             if (gHFSFlavor.fileType == 'fold' || gHFSFlavor.fileType == 'disk')
  410.                                 err = FSpDirectoryCopy (&(gHFSFlavor.fileSpec),&gDropLocFSS,nil,0,true,nil);
  411.                             else
  412.                                 err = FSpFileCopy (&(gHFSFlavor.fileSpec),&gDropLocFSS,nil,nil,0,true);
  413.                         }
  414.                     }
  415.                 }
  416.             }
  417.  
  418.             err2 = DisposeDrag (dragRef);
  419.             if (!err) err = err2;
  420.         }
  421.     }
  422.  
  423.     return err;
  424. }
  425.  
  426. static pascal OSErr ClickInContent (WindowRef whichWindow, const EventRecord *event)
  427. {
  428.     //
  429.     //    See if the click was in the icon. If so, highlight the icon and
  430.     //    let someone else decide whether to drag.
  431.     //
  432.  
  433.     OSErr err = noErr;
  434.  
  435.     if (gIconSuite)
  436.     {
  437.         Rect        iconRect;
  438.         Point        localPoint    = event->where;
  439.         GrafPtr        savedPort    = qd.thePort;
  440.  
  441.         SetPort (whichWindow);
  442.  
  443.         GlobalToLocal (&localPoint);
  444.         MakeIconRect (&iconRect);
  445.  
  446.         if (PtInIconSuite (localPoint,&iconRect,kAlignNone,gIconSuite))
  447.         if (!(err = PlotIconSuite (&iconRect,kAlignNone,kTransformSelected,gIconSuite)))
  448.         {
  449.             OSErr err2 = noErr;
  450.             err = DragOut (event,&iconRect);
  451.             err2 = PlotIconSuite (&iconRect,kAlignNone,kTransformNone,gIconSuite);
  452.             if (!err) err = err2;
  453.         }
  454.  
  455.         SetPort (savedPort);
  456.     }
  457.  
  458.     return err;
  459. }
  460.  
  461. static pascal OSErr MouseDown (const EventRecord *event)
  462. {
  463.     OSErr err = noErr;
  464.  
  465.     WindowRef whichWindow;
  466.     short partCode = FindWindow (event->where,&whichWindow);
  467.  
  468.     Rect boundsRect;
  469.  
  470.     switch (partCode)
  471.     {
  472.         case inContent :
  473.  
  474.             if (whichWindow != FrontWindow ( ))
  475.                 SelectWindow (whichWindow);
  476.             else
  477.                 err = ClickInContent (whichWindow,event);
  478.             break;
  479.  
  480.         case inGoAway :
  481.  
  482.             if (TrackGoAway (whichWindow,event->where))
  483.                 gQuitting = true;
  484.             break;
  485.  
  486.         case inDrag :
  487.  
  488.             boundsRect = qd.screenBits.bounds;
  489.             InsetRect (&boundsRect,4,4);
  490.             DragWindow (whichWindow,event->where,&boundsRect);
  491.             break;
  492.     }
  493.  
  494.     return err;
  495. }
  496.  
  497. static pascal void DrawCenteredString (ConstStr255Param str, short baseLine)
  498. {
  499.     Rect    *portRect        = &(qd.thePort->portRect);
  500.     short    portWidth        = portRect->right - portRect->left,
  501.             stringWidth        = StringWidth (str);
  502.  
  503.     MoveTo (portRect->left + (portWidth / 2) - (stringWidth / 2), baseLine);
  504.     DrawString (str);
  505. }
  506.  
  507. static pascal void AppendOSTypeToString (OSType ost, StringPtr str)
  508. {
  509.     BlockMoveData (&ost,str+*str+1,sizeof(ost));
  510.     *str += sizeof(ost);
  511. }
  512.  
  513. static pascal void AppendHex16ToString (UInt16 ui, StringPtr str)
  514. {
  515.     unsigned char *scan = str + *str + 1;
  516.     unsigned char nybbleIndex = 0;
  517.     static const char hexDigits [ ] = "0123456789ABCDEF";
  518.  
  519.     do
  520.         scan [3 - nybbleIndex] = hexDigits [(ui >> (4 * nybbleIndex)) & 0x0F];
  521.     while (++nybbleIndex < 4);
  522.  
  523.     *str += 4;
  524. }
  525.  
  526. static pascal OSErr UpdateWindow (WindowRef whichWindow)
  527. {
  528.     OSErr err = noErr;
  529.  
  530.     GrafPtr savedPort = qd.thePort;
  531.  
  532.     SetPort (whichWindow);
  533.     BeginUpdate (whichWindow);
  534.  
  535.     EraseRect (&(qd.thePort->portRect));
  536.  
  537.     if (gIconSuite)
  538.     {
  539.         Rect iconRect;
  540.  
  541.         MakeIconRect (&iconRect);
  542.  
  543.         err = PlotIconSuite (&iconRect,kAlignNone,kTransformNone,gIconSuite);
  544.  
  545.         if (err == noMaskFoundErr)
  546.             err = noErr;
  547.  
  548.         if (!err)
  549.         {
  550.             StringPtr str = nil;
  551.             short baseLine = gIconLimitRect.bottom + gFontInfo.ascent;
  552.  
  553.             DrawCenteredString (gHFSFlavor.fileSpec.name, baseLine);
  554.  
  555.             str = (StringPtr) NewPtr (sizeof (Str255));
  556.             if (!(err = MemError ( )))
  557.             {
  558.                 Str15 scratch;
  559.  
  560.                 PLstrcpy (str,"\pvRefNum ");
  561.                 NumToString (gHFSFlavor.fileSpec.vRefNum, scratch);
  562.                 PLstrcat (str,scratch);
  563.                 PLstrcat (str,"\p, parID ");
  564.                 NumToString (gHFSFlavor.fileSpec.parID, scratch);
  565.                 PLstrcat (str,scratch);
  566.                 baseLine += gFontInfo.ascent + gFontInfo.leading;
  567.                 DrawCenteredString (str, baseLine);
  568.  
  569.                 *str = 0;
  570.                 AppendOSTypeToString (gHFSFlavor.fileType, str);
  571.                 PLstrcat (str,"\p ");
  572.                 AppendOSTypeToString (gHFSFlavor.fileCreator, str);
  573.                 PLstrcat (str,"\p 0x");
  574.                 AppendHex16ToString (gHFSFlavor.fdFlags, str);
  575.                 baseLine += gFontInfo.ascent + gFontInfo.leading;
  576.                 DrawCenteredString (str, baseLine);
  577.  
  578.                 DisposePtr ((Ptr) str);
  579.                 if (!err) err = MemError ( );
  580.             }
  581.         }
  582.     }
  583.  
  584.     EndUpdate (whichWindow);
  585.     SetPort (savedPort);
  586.  
  587.     return err;
  588. }
  589.  
  590. static pascal void InvalWindow (WindowRef wRef)
  591. {
  592.     //    "post" an update event for the entire window
  593.  
  594.     GrafPtr savedPort = qd.thePort;
  595.     SetPort (wRef);
  596.     InvalRect (&(qd.thePort->portRect));
  597.     SetPort (savedPort);
  598. }
  599.  
  600. static pascal OSErr RebuildIcon (WindowRef wRef)
  601. {
  602.     OSErr err = noErr;
  603.  
  604.     if (!gIconSuite && gHFSFlavor.fileSpec.vRefNum)
  605.         if (!(err = GetIconSuiteFromFinder (&(gHFSFlavor.fileSpec),&gIconSuite)))
  606.             InvalWindow (wRef);
  607.  
  608.     return err;
  609. }
  610.  
  611. static pascal OSErr NullEvent (WindowRef wRef)
  612. {
  613.     OSErr err = noErr;
  614.  
  615.     if (!(err = RebuildIcon (wRef)))
  616.         ;
  617.  
  618.     return err;
  619. }
  620.  
  621. static pascal OSErr OneEvent (const EventRecord *event, WindowRef wRef)
  622. {
  623.     OSErr err = noErr;
  624.  
  625.     switch (event->what)
  626.     {
  627.         case kHighLevelEvent :
  628.  
  629.             err = AEProcessAppleEvent (event);
  630.             break;
  631.  
  632.         case mouseDown :
  633.  
  634.             err = MouseDown (event);
  635.             break;
  636.  
  637.         case updateEvt :
  638.  
  639.             UpdateWindow ((WindowRef) (event->message));
  640.             break;
  641.  
  642.         case nullEvent :
  643.  
  644.             NullEvent (wRef);
  645.             break;
  646.     }
  647.  
  648.     return err;
  649. }
  650.  
  651. static pascal void LoopEvents (WindowRef wRef)
  652. {
  653.     do
  654.     {
  655.         OSErr err;
  656.  
  657.         EventRecord event;
  658.         WaitNextEvent (everyEvent,&event,-1,nil);
  659.         err = OneEvent (&event,wRef);
  660.         InitCursor ( );
  661.     }
  662.     while (!gQuitting);
  663. }
  664.  
  665. static pascal OSErr ShowDragHiliteWindow (DragReference dragRef, WindowRef wRef)
  666. {
  667.     OSErr err = noErr;
  668.  
  669.     RgnHandle hiliteRgn = NewRgn ( );
  670.     if (!hiliteRgn)
  671.         err = nilHandleErr;
  672.     else
  673.     {
  674.         RectRgn (hiliteRgn,&(wRef->portRect));
  675.         err = ShowDragHilite (dragRef,hiliteRgn,true);
  676.         DisposeRgn (hiliteRgn);
  677.     }
  678.  
  679.     return err;
  680. }
  681.  
  682. static pascal OSErr ApproveDragReference (DragReference theDragRef, Boolean *approved)
  683. {
  684.     //
  685.     //    We accept one item and one item only.
  686.     //    It must have either 'flavorTypeHFS' or 'flavorTypePromiseHFS'.
  687.     //    If the user held the option key down when beginning the drag
  688.     //    or is holding the option key down when the drag enters, we
  689.     //    accept only 'flavorTypePromiseHFS'.
  690.     //
  691.     //    Note that if a flavor can't be found, it's not really an
  692.     //    error; it only means the flavor wasn't there and we should
  693.     //    not accept the drag. Therefore, we translate 'badDragFlavorErr'
  694.     //    into a 'false' value for '*approved'.
  695.     //
  696.  
  697.     OSErr err = noErr;
  698.  
  699.     UInt16            itemCount;
  700.     DragAttributes    dragAttrs;
  701.     short            currentModifers,
  702.                     mouseDownModifiers;
  703.  
  704.     *approved = false;
  705.  
  706.     if (!(err = GetDragAttributes (theDragRef,&dragAttrs)))
  707.     if (!(dragAttrs & dragInsideSenderWindow))
  708.     if (!(err = CountDragItems (theDragRef,&itemCount)) && itemCount == 1)
  709.     if (!(err = GetDragItemReferenceNumber (theDragRef,1,&gItemRef)))
  710.     if (!(err = GetDragModifiers (theDragRef,¤tModifers,&mouseDownModifiers,nil)))
  711.     {
  712.         if ((currentModifers & optionKey) || (mouseDownModifiers & optionKey))
  713.             err = badDragFlavorErr; // pretend to have searched and not found
  714.         else
  715.         {
  716.             FlavorFlags flavorFlags;
  717.             err = GetFlavorFlags (theDragRef,gItemRef,flavorTypeHFS,&flavorFlags);
  718.         }
  719.  
  720.         if (!err)
  721.             *approved = true;
  722.         else if (err == badDragFlavorErr)
  723.         {
  724.             PromiseHFSFlavor    promiseHFSFlavor;
  725.             Size                dataSize;
  726.  
  727.             dataSize = sizeof (promiseHFSFlavor);
  728.             err = GetFlavorData (theDragRef,gItemRef,flavorTypePromiseHFS,&promiseHFSFlavor,&dataSize,0);
  729.  
  730.             if (err == badDragFlavorErr)
  731.                 err = noErr; // *approved = false;
  732.             else if (!err)
  733.             {
  734.                 if (dataSize != sizeof (promiseHFSFlavor))
  735.                     err = cantGetFlavorErr;
  736.                 else
  737.                     *approved = true;
  738.             }
  739.         }
  740.     }
  741.  
  742.     return err;
  743. }
  744.  
  745. static pascal OSErr MyDragTrackingHandler
  746.     (DragTrackingMessage message, WindowPtr theWindow, void *, DragReference theDragRef)
  747. {
  748.     Boolean approved;
  749.  
  750.     switch (message)
  751.     {
  752.         case dragTrackingEnterWindow    :
  753.  
  754.             if (!ApproveDragReference (theDragRef,&approved) && approved)
  755.             if (!ShowDragHiliteWindow (theDragRef,theWindow))
  756.                 gApprovedDragRef = theDragRef;
  757.             break;
  758.  
  759.         case dragTrackingInWindow        :
  760.  
  761.             // do nothing
  762.             break;
  763.  
  764.         case dragTrackingLeaveWindow    :
  765.  
  766.             (void) HideDragHilite (theDragRef);
  767.             // fall thru
  768.  
  769.         default :
  770.  
  771.             gApprovedDragRef = nil;
  772.             break;
  773.     }
  774.  
  775.     return noErr; // there's no point in confusing Drag Manager or its caller
  776. }
  777.  
  778. static pascal OSErr MyDragReceiveHandler (WindowPtr, void *, DragReference theDragRef)
  779. {
  780.     //
  781.     //    Receive a drag.
  782.     //
  783.     //    First attempt to grab flavorTypePromiseHFS data
  784.     //    without specifying a drop location to induce Find File to
  785.     //    reveal its secrets. If we're not dealing
  786.     //    with Find File, make sure there's a folder to receive
  787.     //    the new file and and receive the file there.
  788.     //
  789.     //    Once the file's been received, we need a null event sent
  790.     //    to our event loop because that's when we check to see if
  791.     //    gHFSFlavor has changed. Since Drag Manager callbacks run
  792.     //    outside the event loop, we might otherwise only receive
  793.     //    a null event after quite a long time, especially if we
  794.     //    specify a long sleep time when calling WaitNextEvent.
  795.     //    (See RebuildIcon and its caller.)
  796.     //
  797.  
  798.     OSErr err = dragNotAcceptedErr;
  799.  
  800.     if (theDragRef == gApprovedDragRef)
  801.     {
  802.         err = GetHFSFlavorFromDragReference (gApprovedDragRef,gItemRef,&gHFSFlavor);
  803.  
  804.         if (err == badDragFlavorErr)
  805.         {
  806.             err = ReceivePromisedFile (gApprovedDragRef,gItemRef,&gHFSFlavor,nil);
  807.  
  808.             if (err == paramErr) // it's not from Find File
  809.             {
  810.                 if (*(gDropLocation.name))
  811.                     err = noErr;
  812.                 else
  813.                 {
  814.                     // real programs don't use string constants
  815.                     err = FSMakeFSSpec (0,0,"\pDropSpool",&gDropLocation);
  816.                     if (err == fnfErr)
  817.                     {
  818.                         long createdDirID;
  819.                         // specify smRoman because of the string constant
  820.                         err = FSpDirCreate (&gDropLocation,smRoman,&createdDirID);
  821.                     }
  822.                 }
  823.  
  824.                 if (!err)
  825.                     err = ReceivePromisedFile
  826.                         (gApprovedDragRef,gItemRef,&gHFSFlavor,&gDropLocation);
  827.             }
  828.         }
  829.  
  830.         if (!err)
  831.         {
  832.             if (!(err = WakeUpCurrentProcess ( )))
  833.             {
  834.                 if (gIconSuite)
  835.                 {
  836.                     DisposeIconSuite (gIconSuite,true);
  837.                     gIconSuite = nil;
  838.                 }
  839.             }
  840.         }
  841.     }
  842.  
  843.     return err;
  844. }
  845.  
  846. static pascal OSErr CreateFinderDragProWindow (WindowRef *wRef)
  847. {
  848.     OSErr err = noErr;
  849.  
  850.     DragReceiveHandlerUPP    dragReceiveHandlerUPP    = nil;
  851.     DragTrackingHandlerUPP    dragTrackingHandlerUPP    = nil;
  852.     Rect                    boundsRect;
  853.     unsigned short            top;
  854.  
  855.     *wRef = nil;
  856.  
  857.     top = qd.screenBits.bounds.top + GetMBarHeight ( ) + 20;
  858.     SetRect (&boundsRect, 20, top + 50, 200, top + 150);
  859.  
  860.     *wRef = NewCWindow
  861.         (nil, &boundsRect, LMGetCurApName ( ), true, noGrowDocProc, (WindowRef) -1, true, 0);
  862.  
  863.     if (!*wRef)
  864.         err = nilHandleErr;
  865.     else
  866.     {
  867.         dragTrackingHandlerUPP = NewDragTrackingHandlerProc (MyDragTrackingHandler);
  868.  
  869.         if (!dragTrackingHandlerUPP)
  870.             err = nilHandleErr;
  871.         else
  872.         {
  873.             dragReceiveHandlerUPP = NewDragReceiveHandlerProc (MyDragReceiveHandler);
  874.  
  875.             if (!dragReceiveHandlerUPP)
  876.                 err = nilHandleErr;
  877.             else if (!(err = InstallTrackingHandler (dragTrackingHandlerUPP,*wRef,nil)))
  878.             {
  879.                 err = InstallReceiveHandler (dragReceiveHandlerUPP,*wRef,nil);
  880.                 if (err)
  881.                     RemoveTrackingHandler (dragTrackingHandlerUPP,*wRef);
  882.                 else
  883.                 {
  884.                     GrafPtr savedPort = qd.thePort;
  885.                     SetPort (*wRef);
  886.                     TextSize (9);
  887.                     GetFontInfo (&gFontInfo);
  888.                     gIconLimitRect = qd.thePort->portRect;
  889.                     gIconLimitRect.bottom -= 3 * (gFontInfo.ascent + gFontInfo.leading + gFontInfo.descent);
  890.                     SetPort (savedPort);
  891.                 }
  892.             }
  893.         }
  894.     }
  895.  
  896.     if (err)
  897.     {
  898.         if (*wRef)                        DisposeWindow (*wRef);
  899.         if (dragTrackingHandlerUPP)        DisposeRoutineDescriptor (dragTrackingHandlerUPP);
  900.         if (dragReceiveHandlerUPP)        DisposeRoutineDescriptor (dragReceiveHandlerUPP);
  901.     }
  902.  
  903.     return err;
  904. }
  905.  
  906. void main (void)
  907. {
  908.     if (InitMac ( ))
  909.         SysBeep (10);
  910.     else
  911.     {
  912.         WindowRef wRef;
  913.         (void) InstallBogusFinderEventHandler ( );
  914.         CreateFinderDragProWindow (&wRef);
  915.         LoopEvents (wRef);
  916.     }
  917. }
  918.